using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using MessageLibrary;
using System.Resources;
using System.Reflection;
using System.IO;
using System.Collections;

namespace ChatClient
{
    public delegate void ControlUpdaterDelegate(object o);

    public partial class ClientForm : Form
    {
        #region
        string userName ;
        Client c;
        TcpClient tcp;
        IPAddress address ;
        int port;
        bool pendingMessage = false;
        #endregion


        public ClientForm()
        {
            InitializeComponent();
            //dissables the log out button until the client
            //is connected
            btnLogOut.Enabled = false;           
        }

        private void btnLogin_Click(object sender, EventArgs e)
        {
            //starts log in prcedures
            LogIn();
        }


        /// <summary>
        /// the work method for the backgroung worker
        /// that preforms the log-in procedure for
        /// the client
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {        
           
                //updating status through the main thread 
                logInWorker.ReportProgress(3);

                tcp = new TcpClient();
                try
                {
                    //tries to connect to the server
                    tcp.Connect(address, port);

                }
                catch (SocketException exp)
                {
                    //reports an error in the connection
                    MessageBox.Show("Could not log in to server,\nPlease check server details.", "Log-In Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                    logInWorker.ReportProgress(2);
                    return;

                }
                catch (Exception exp)
                {
                    //reports any other error
                    MessageBox.Show(exp.Message,exp.Source);
                    logInWorker.ReportProgress(2);
                    return;
                }
                //creates the client object for this connection
                c = new Client(tcp);
                c.UserName = userName;
                //registers for the clients events
                c.NewMessage += new Client.MessageEventHandler(c_NewMessage);
                c.Finished += new Client.FinishedEventHandler(c_Finished);


                //send a login message with the new username
                LogInMessage msg = new LogInMessage(userName);
                StartSendThread(msg);  

                //start the listening thread
                c.Start();

                //updating status through the main thread                 
                logInWorker.ReportProgress(1);            
                                  
        }
        /// <summary>
        /// the method that runs when the client.Finished
        /// event occurs.
        /// </summary>
        /// <param name="c">the client</param>
        void c_Finished(Client c)
        {
            
            if (this.InvokeRequired)
            {
                Client.FinishedEventHandler fev = new Client.FinishedEventHandler(c_Finished);
                this.Invoke(fev, c);

            }
            else
            {
                //updates display icons & buttons
                this.Icon = ResIcons.Offline;
                btnLogIn.Enabled = true;
                btnLogOut.Enabled = false;
                lblstatus.Text = "Disconnected";

                //updates the client
                rtfBoxChat.SelectionStart = rtfBoxChat.Text.Length;
                rtfBoxChat.SelectedText = DateTime.Now.ToShortTimeString();
                rtfBoxChat.SelectedText = " You have been logged off.\n";
                rtfBoxChat.ScrollToCaret();
            }
            
        }
        /// <summary>
        /// the method that runs when the client.NewMessage
        /// event occurs
        /// </summary>
        /// <param name="c"></param>
        /// <param name="msg"></param>
        void c_NewMessage(Client c, AbsMessage msg)
        {
            if (msg!=null)
            {
                //handles the new message with the chat's
                //rich text box
                msg.HandleMessage(rtfBoxChat, null);
            }
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            
            switch (e.ProgressPercentage)
            {
                case 1:

                    lblstatus.Text = "Connected";
                    this.Icon = ResIcons.online;
                    btnLogOut.Enabled = true;
                    rtfBoxInput.Focus();
                    break;
                case 2:
                    lblstatus.Text = "LogIn failed";
                    btnLogIn.Enabled = true;
                    break;
                case 3:
                    lblstatus.Text = "connecting";
                    break;
                default:
                    break;
            }
        }

        private void btnLogOut_Click(object sender, EventArgs e)
        {
            //checks if the client is running 
            if (c != null && c.Running)
            {
                //creates a new LogOutMessgae
                StartSendThread(new LogOutMessage(c.UserName));
                btnLogOut.Enabled = false;
                //waits until the log out message ha been sent
                while (pendingMessage)
                {
                    Thread.Sleep(100);
                    Application.DoEvents();
                }
                //stops the client from listening
                c.Stop();
            }            
        }
        /// <summary>
        /// sends a messgae through a different thread
        /// </summary>
        /// <param name="o">the message</param>
        private void SendMessage(object o)
        {
            //checks if the client is running
            if (c!=null && c.Running)
            {   
                //send message
                if (o is AbsMessage)
                {
                    c.SendMessage((AbsMessage)o);
                }
                else MessageBox.Show("Could Not send message");
            }
            else MessageBox.Show("You are not connected\nPlease Log in","Connect First",MessageBoxButtons.OK,MessageBoxIcon.Error);
            //informs that the message was sent
            pendingMessage = false;

            
        }

        private void ClientForm_SizeChanged(object sender, EventArgs e)
        {
            ///re-sets the width of the inputBox according to
            ///the split container new width
            rtfBoxInput.Width = splitContainer1.Panel2.Width - btnSend.Width - 1;
        }

        private void logInWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            //checks if there was an error during login
            if (e.Error != null)
            {
                MessageBox.Show(e.Error.Message, e.Error.Source);
            }
            
        }
        /// <summary>
        /// the method that takes the rtf from the inputRtfBox
        /// and sends it away.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSend_Click(object sender, EventArgs e)
        {
            //checks if there's something to send
            if (rtfBoxInput.Text==string.Empty)
            {
                MessageBox.Show("Please type an input to send");
                return;
            }
            //creates a new content message object
            //with the rft to send
            ContentMessage cm = new ContentMessage(userName, rtfBoxInput.Rtf);
            StartSendThread(cm);

            //resets the inputBox
            rtfBoxInput.Clear();
            rtfBoxInput.Focus();
        }

        private void StartSendThread(AbsMessage msg)
        {
            //marks a flag for out going message
            pendingMessage = true;
            //starts a new thread with an object (the message)
            //to send.
            Thread t = new Thread(new ParameterizedThreadStart(SendMessage));
            t.IsBackground = true;
            t.Start(msg);
        }

        private void ClientForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            //asks the user for validation
            DialogResult dr =MessageBox.Show("Are You Sure You Want To Leave?", "Leaving So Soon?", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            //cancels if needed
            if (dr==DialogResult.No)
            {
                e.Cancel = true;
                return;
            }
            //informs the server about logging off
            if (c!=null && c.Running)
            {
                StartSendThread(new LogOutMessage(c.UserName));
            }
            

        }

        private void ClientForm_Load(object sender, EventArgs e)
        {
            this.Icon = ResIcons.Offline;
            //hides the emoticons panel
            emoticonsPanel1.Hide();
            //starts log in procedures
            LogIn();
            //load the emoticons from the resource file
            //to the panel
            LoadEmoticons();
        }

        private void LogIn()
        {
            //creates a new log in  form,
            //transfers former user data if available.
            LogInDialog logInDialog = new LogInDialog(userName, port, address, rtfBoxInput.ForeColor);
            logInDialog.ShowDialog();

            //checks the login dialog result
            if (logInDialog.LogInResult==DialogResult.OK)
            {
                //transfers the user data to local variables
                userName = logInDialog.UserName;
                address = logInDialog.Address;
                port = logInDialog.Port;
                rtfBoxInput.ForeColor = logInDialog.TextColor;
                //updates the form
                btnColor.ForeColor = rtfBoxInput.ForeColor;
                btnLogIn.Enabled = false;
                //starts login thread
                logInWorker.RunWorkerAsync();

            }
            //disposes of the login form
            logInDialog.Dispose();

        }

        private void helpToolStripButton_Click(object sender, EventArgs e)
        {
            //checks if emoticon panel is already visable
            if (emoticonsPanel1.Visible)
            {
                emoticonsPanel1.Hide();
            }
            else
            {
                emoticonsPanel1.ShowControl();
                emoticonsPanel1.Focus();
            }
            
        }

        private void ClientForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            //waits untill all messages has been sent
            while (pendingMessage)
            {
                Thread.Sleep(50);
                Application.DoEvents();
            }
            //stops the listening thread
            if (c != null) c.Stop();
            if (tcp != null) tcp.Close();

        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }
        /// <summary>
        /// opens a font dialog and sets the selected
        /// font to the input text
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnFont_Click(object sender, EventArgs e)
        {
            if (fontDialog1.ShowDialog() == DialogResult.OK)
            {
                //check if there is a selected text to change
                if (rtfBoxInput.SelectedText != string.Empty)
                {
                    //changes just the selected text
                    rtfBoxInput.SelectionFont = fontDialog1.Font;
                }
                    //changes all the text in the input box
                else rtfBoxInput.Font = fontDialog1.Font;
            }
        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            ///show a color dialog to set the color
            ///of the input text
            if (colorDialog1.ShowDialog() == DialogResult.OK)
            {
                //checks and rejects if the chosen color is white
                if (colorDialog1.Color==Color.White)
                {
                    MessageBox.Show("Not White Please");
                    return;
                }
                //check if there is a selected text to change
                if (rtfBoxInput.SelectedText != string.Empty)
                {
                    //changes just the selected text
                    rtfBoxInput.SelectionColor = colorDialog1.Color;
                    //rtfBoxInput.SelectionBackColor = colorDialog1.Color;
                }
                //changes all the text in the input box
                else
                {
                    rtfBoxInput.ForeColor = colorDialog1.Color;
                    btnColor.ForeColor = colorDialog1.Color;
                }
            }

        }

        /// <summary>
        /// Load the images from a resouce file 
        /// to the emoticon panel
        /// </summary>
        private void LoadEmoticons()
        {
            //gets a stream from the program resource file
            Assembly assembly = Assembly.GetExecutingAssembly();            
            Stream stream = assembly.GetManifestResourceStream("ChatClient.Emoticons.resources");
            //strats a resourceReader to extract the images
            ResourceReader rr = new ResourceReader(stream);
            //a container for images
            ImageList ilist = new ImageList();
            //inserts each image to the imageList
            foreach (DictionaryEntry d in rr)
            {
                if (d.Value is Image)
                {

                    ilist.Images.Add(d.Key.ToString(), (Image)d.Value);
                }
            }
            //loads the imageList to the emoticon panel
            emoticonsPanel1.LoadImageList(ilist);
            //registers to the IconClick event that select
            //an image from the panel
            emoticonsPanel1.IconClick += new EventHandler(emoticonsPanel1_IconClick);

            //disposes of the resources
            rr.Close();
            stream.Close();

        }

        void emoticonsPanel1_IconClick(object sender, EventArgs e)
        {
            //checks if the recieved item is an image
            if (sender is Image)
            {
                //saves the current item in the clipboard 
                IDataObject o = Clipboard.GetDataObject();
                //copies the selected image to the clipborad
                Clipboard.SetImage((Image)sender);
                //pastes the image to the inputBox
                rtfBoxInput.Paste();
                //restores the original item to the clipboard
                Clipboard.SetDataObject(o);
            }
        }

        private void emoticonsPanel1_Leave(object sender, EventArgs e)
        {
            //hides the panel when the control loses focus
            emoticonsPanel1.Hide();
        }

    }

        
}